#ifndef _GETOPT_HELPER_H__
#define _GETOPT_HELPER_H__

#include <limits.h>
#include <stddef.h>

#include <metalang99.h>
#include <metalang99/bool.h>
#include <metalang99/gen.h>
#include <metalang99/ident.h>
#include <metalang99/lang.h>
#include <metalang99/list.h>
#include <metalang99/util.h>
#include <metalang99/variadics.h>

#if ML99_MAJOR < 1 && ML99_MINOR < 8
#	erroe "metalang99 >= 1.8 required"
#endif /* ML99_CHAR_LIT add in 1.8 */

#include "version.h"

#ifndef GETOPT_HELPER_EXIT_FAILURE
#	define GETOPT_HELPER_EXIT_FAILURE (!0)
#endif
extern const char *__progname; /* NOLINT(bugprone-reserved-identifier) */

enum {
	GETOPT_HELP_CHAR = -2,
	GETOPT_VERSION_CHAR = -3,
};

enum flag {
	NO = 0,
	YES = !0,
};

#define GETOPT_HELPER_VAR_NAME_ARITY	  2
#define GETOPT_HELPER_VAR_NAME_IMPL(x, y) v(y##flag = (x))

#define INIT_FLAGS(VAL, ...)                                                  \
	ML99_LIST_EVAL_COMMA_SEP(                                             \
	    ML99_listMap(ML99_appl(v(GETOPT_HELPER_VAR_NAME), v(VAL)),        \
			 ML99_list(v(__VA_ARGS__))))

#define GETOPT_HELPER_IS_FLAG_ARITY 2
#define GETOPT_HELPER_IS_FLAG_IMPL(VAL, FLAG)                                 \
	v(case ML99_CHAR_LIT(FLAG) : FLAG##flag = (VAL); break)

#define IS_FLAG(VAL, FLAG)                                                    \
	ML99_EVAL(ML99_semicoloned(                                           \
	    ML99_call(v(GETOPT_HELPER_IS_FLAG), v(VAL), v(FLAG))))            \
	ML99_TRAILING_SEMICOLON()

#define IS_FLAGS(VAL, ...)                                                    \
	ML99_EVAL(ML99_variadicsForEach(                                      \
	    ML99_compose(v(ML99_semicoloned),                                 \
			 ML99_appl(v(GETOPT_HELPER_IS_FLAG), v(VAL))),        \
	    v(__VA_ARGS__)))                                                  \
	ML99_TRAILING_SEMICOLON()

#define GETOPT_HELP_OPTION_DECL "help", no_argument, NULL, GETOPT_HELP_CHAR
#define GETOPT_VERSION_OPTION_DECL                                            \
	"version", no_argument, NULL, GETOPT_VERSION_CHAR

/* clang-format off */
#define GETOPT_END_DECL                                                       \
	{ GETOPT_HELP_OPTION_DECL }, { GETOPT_VERSION_OPTION_DECL },          \
	    { NULL, 0, NULL, 0 }
/* clang-format on */

#define EXPAND_PROGNAME(N) /* note: N must be in 1-9 range */                 \
	ML99_LIST_EVAL_COMMA_SEP(ML99_listTail(                               \
	    ML99_list(ML99_times(ML99_if(ML99_isDigit(v(N)), v(N), v(1)),     \
				 v(, __progname)))))

#define usage(S)                                                              \
	do {                                                                  \
		printf(usage_str, EXPAND_PROGNAME(NFORMAT));                  \
		putchar('\n');                                                \
		return (S);                                                   \
	} while (0)

/* initial value for long options enum */
#define GETOPT_LONG_ENUM_INIT (CHAR_MAX + 1)

#define GETOPT_END_CASE                                                       \
case GETOPT_VERSION_CHAR: {                                                   \
	print_version();                                                      \
	return 0;                                                             \
}                                                                             \
case GETOPT_HELP_CHAR: {                                                      \
	usage(0);                                                             \
	break;                                                                \
}                                                                             \
default: {                                                                    \
	usage(GETOPT_HELPER_EXIT_FAILURE);                                    \
}

#define GETOPT_NO_EXTRA_ARGS(ARGC)                                            \
	do {                                                                  \
		if (!!((ARGC)-optind)) usage(!0);                             \
	} while (0)

#endif /* _GETOPT_HELPER_H__ */
